Skip to content

fix(cli): fail closed on mid-pagination 404 in project resolution - #146

Merged
juangaitanv merged 1 commit into
mainfrom
fix/projects-404-mid-pagination
Aug 3, 2026
Merged

fix(cli): fail closed on mid-pagination 404 in project resolution#146
juangaitanv merged 1 commit into
mainfrom
fix/projects-404-mid-pagination

Conversation

@juangaitanv

Copy link
Copy Markdown
Contributor

Bug

fetch_projects_page maps every HTTP 404 to Ok(None), and the pagination loop in resolve_project_by_repo treated that as a clean "no match" regardless of which page it came from:

let Some(parsed) = fetch_projects_page(url, repo_path, page)? else {
    return Ok(None);
};

So a 404 on page 2+ silently discarded the exact matches already accumulated from page 1 and returned a clean miss. resolve_project then falls back to the checkout basename, so list/wait can query an unrelated same-basename project and exit 0 — the exact failure mode this hardening code exists to prevent.

The concurrent-deletion scenario

The backend filters repo_url__icontains over a paginated list. If a project is deleted between the page-1 and page-2 requests, the filtered result set shrinks and Django's paginator 404s the now out-of-range page. Nothing is wrong with the CLI's request — but the walk was incomplete, and the answer is no longer trustworthy.

Fix

Page 1 keeps its soft Ok(None): that 404 is the intentional compatibility path for a backend that does not have the /projects endpoint at all, and the caller correctly falls back to CWD-name resolution there. Any page after the walk has started is now a hard Err — the same fail-closed posture as the PROJECTS_MAX_PAGES ceiling right below it.

Test

resolve_project_by_repo_mid_pagination_404_is_hard_err: page 1 returns total_pages: 2 with the exact match, page 2 returns 404 → Err mentioning page 2 and pagination. spawn_paged_projects_stub gained a status-aware variant, mirroring the existing spawn_projects_stub / spawn_projects_stub_status pair. resolve_project_by_repo_empty_and_404_are_soft_none still passes.

cargo test: 595 passed, 0 failed.

Reference

Review comment on #138: #138 (comment)

fetch_projects_page maps every 404 to Ok(None), and the pagination loop in
resolve_project_by_repo turned that into a clean "no match" on any page. A
404 on page 2+ — Django pagination when a concurrent delete shrinks the
filtered set — therefore discarded the exact matches already collected from
page 1, and the caller fell back to the checkout basename, letting list/wait
query an unrelated same-basename project and exit 0.

The page-1 404 stays a soft None: that is the intentional compatibility path
for a backend without the /projects endpoint. Any later page is now a hard
error.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No actionable findings. I verified the merge-base diff and traced the fetch_projects_page 404 sentinel through resolve_project_by_repo and its fatal list/wait call paths. Page 1 remains the old-backend compatibility soft miss, while every page 2+ 404 now returns before collected matches can be discarded into the legacy-name fallback. The focused test exercises that exact transition and asserts the hard error; formatting/diff checks pass, and CI's strict clippy, dependency audit, and 595-test coverage gate are green. I found no production blocker and approve this change.

Open in Web View Automation 

Sent by Cursor Automation: pr-flow

@corgea-security corgea-security left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review risk: 1/5.

No actionable findings. The change correctly preserves the page-1 compatibility behavior while treating later 404 responses as pagination failures, with focused regression coverage.

No critical or high-priority changes were found.

@corgea-security corgea-security added the dennis-reviewed Dennis completed an automated review label Aug 3, 2026

@corgea-security corgea-security left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved by Dennis: high policy risk and automated risk 1/5.

@juangaitanv
juangaitanv merged commit db6cec0 into main Aug 3, 2026
19 checks passed
@juangaitanv
juangaitanv deleted the fix/projects-404-mid-pagination branch August 3, 2026 16:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dennis-reviewed Dennis completed an automated review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants